/**
 * api工具
 * @author luguoxiang
 * @date 2021-09-07
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 */
import xhr from './xhr';
import request from './request';
import { Arrayable, Map } from '../../types';
import message from '../message';
import { toPromise } from '../data';

export * from './webSocket';
export * from './sse';
export { xhr, request };

export declare type QueryParams = {
  /**
   * 万能查询
   * 例：{ 'id-eq': 12, 'name-like': '六哥', 'age-ge': 18 }
   */
  $soarQuery?: {
    /**
     * 需要返回的字段
     */
    fields?: string[] | string;
    /**
     * 需要排序的字段，逆序字段前面加“-”
     */
    orderBys?: string[] | string;
    /**
     * 字段过滤
     * <= 转为 le；< 转为 lt；>= 转为 ge；> 转为 gt；
     * ** 转为 like；*= 转为 likeLeft；=* 转为 likeRight；!* 转为 notLike；
     * != 转为 ne；= 转为 eq；
     * !(v1,v2,v3...,vn) 转为 notIn；(v1,v2,v3...,vn) 转为 in；
     * !{v1,v2} 转为 notBetween；{v1,v2} 转为 between；
     * 例1：age{18,48} => age between 18 and 48
     * 例2：id(1,3,8,9) => id in(1,3,8,9)
     * 例3：name**admin => name like admin
     * 例4：code eq admin => code = admin
     */
    filters?: string[] | string;
    /**
     * 过滤参数
     * 例：id-eq: 12
     */
    [p: string]: any;
  };
  /**
   * 分页页码
   */
  pageNo?: number;
  /**
   * 分页大小
   */
  pageSize?: number;
  /**
   * 查询类型，all：默认方式，list：仅返回数据，total：仅返回总记录数
   */
  queryType?: 'all' | 'list' | 'total';
  [p: string]: any;
};

export declare type GetParams = {
  (val: any): QueryParams;
};

/**
 * 转化为异步加载器
 * @param data
 * @param config 请求方式
 */
export function toAsyncLoader(data: any, config: any = 'GET') {
  if (typeof data === 'string') {
    if (typeof config === 'string') {
      config = { method: config };
    }
    return (params: any) => request({ ...config, params, url: data });
  }
  if (typeof data === 'function') {
    return (...params: any[]) => {
      return toPromise(data(...params));
    };
  }
  return () => Promise.resolve(data);
}

/**
 * 生成 api 对象
 * @param basePath 基础路径（后端 controller 路径）
 * @param subject 业务主体（如：用户、角色、权限等）
 * @returns {*}
 */
export function createApi<T>(basePath: string, subject = '') {
  const apiObj = {
    /**
     * 接口路径
     */
    urls: {
      create: `${basePath}/create`,
      update: `${basePath}/update`,
      remove: `${basePath}/remove`,
      detail: `${basePath}/detail`,
      query: `${basePath}/query`,
      exist: `${basePath}/exist`,
      exports: `${basePath}/export`,
      imports: `${basePath}/import`,
      updateDisabled: `${basePath}/updateDisabled`,
      updateSort: `${basePath}/updateSort`,
      resetSort: `${basePath}/resetSort`,
    },
    /**
     * 创建
     * @param data 数据
     * @param config 配置
     */
    create(data: T, config?: any): Promise<any> {
      if (!data) {
        message.error('缺少数据');
        return Promise.reject();
      }
      return request.post(apiObj.urls.create, {
        title: `新增${subject}`,
        ...config,
        data,
      });
    },
    /**
     * 修改
     * @param data 数据
     * @param config 配置
     */
    update(data: T, config?: any): Promise<any> {
      if (!data) {
        message.error('缺少数据');
        return Promise.reject();
      }
      return request.post(apiObj.urls.update, {
        title: `修改${subject}`,
        ...config,
        data,
      });
    },
    /**
     * 删除
     * @param ids id 或 id数组
     * @param config 配置
     */
    remove(ids: Arrayable<string | number>, config?: any): Promise<any> {
      // @ts-ignore
      if (!ids || ids.length === 0) {
        message.error('请选择要删除的数据');
        return Promise.reject();
      }
      return request.post(apiObj.urls.remove, {
        title: `删除${subject}`,
        ...config,
        data: ids,
      });
    },
    /**
     * 查询详情
     * @param id id
     * @param config 配置
     */
    detail(id: string | number, config?: any): Promise<T> {
      return request.get(apiObj.urls.detail, {
        title: `获取${subject}`,
        params: { id },
        ...config,
      });
    },
    /**
     * 分页查询
     * @param params 参数
     * @param config 配置
     */
    query(params?: QueryParams, config?: any): Promise<any> {
      return request.get(apiObj.urls.query, {
        title: `查询${subject}`,
        ...config,
        params,
      });
    },
    /**
     * 导出
     * @param params 参数
     * @param filename 文件名
     */
    exports(params?: QueryParams, filename?: string) {
      request.download(apiObj.urls.exports, { params }, filename);
    },
    /**
     * 导入
     */
    imports(file: File) {
      request.upload(apiObj.urls.imports, file);
    },
    /**
     * 更新排序
     * @param id id
     * @param sort 排序值
     */
    updateSort(id: string | number, sort: number): Promise<any> {
      return request.post(apiObj.urls.updateSort, {
        title: `修改${subject}排序`,
        params: { id, sort },
      });
    },
    /**
     * 重置排序
     */
    resetSort(): Promise<any> {
      return request.post(apiObj.urls.resetSort, {
        title: `重置${subject}排序`,
      });
    },
    /**
     * 查询列表
     * @param params 参数
     * @param config 配置
     */
    list(params?: QueryParams, config?: any): Promise<any> {
      params ??= {};
      params.pageSize ??= 99999;
      params.queryType = 'list';
      return apiObj.query(params, config).then((res) => {
        return Array.isArray(res) ? res : res.list;
      });
    },
    /**
     * 根据id查询
     */
    listByIds(ids: Arrayable<string | number>, fields: Arrayable<string>) {
      return apiObj.list({ $soarQuery: { 'id-in': ids, fields } });
    },
    /**
     * 统计总数
     * @param params 参数
     * @param config 配置
     */
    count(params?: QueryParams, config?: any): Promise<any> {
      params ??= {};
      params.queryType = 'total';
      return apiObj.query(params, config).then((res) => {
        return typeof res === 'number' ? res : res.total;
      });
    },
    /**
     * 查询是否存在
     * @param params 参数
     * @param config 配置
     */
    exist(params?: QueryParams, config?: any): Promise<any> {
      // @ts-ignore
      return request.get(apiObj.urls.exists, {
        title: `查询${subject}是否已存在`,
        ...config,
        params,
      });
    },
    /**
     * 保存或更新
     * @param data
     * @param isUpdate
     */
    saveOrUpdate(data: T, isUpdate = false): Promise<any> {
      if (isUpdate) {
        return apiObj.update(data);
      }
      return apiObj.create(data);
    },
    /**
     * 创建校验
     */
    createValidator(getParams: GetParams, message = '数据已存在') {
      return {
        validator: (val: any) => {
          const params = getParams(val);
          if (params) {
            params.queryType = 'total';
            return this.query(params).then((res: { total: number }) => {
              return res.total === 0;
            });
          }
          return true;
        },
        message,
      };
    },
  };
  return apiObj;
}
